﻿using System;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Entity.Interface;
using RayDen.RayEngine.Core.Types;

namespace RayDen.RayEngine.Data
{
    using Vector3 = Vector;

    public class SimpleCamera : ICamera
    {


        protected Vector3 mRight;
        protected Vector3 mUp;
        protected Vector3 mLook;

        protected float mUlength = 0.0f;
        protected float mVlength = 0.0f;
        protected int w, h;

        public SimpleCamera(int width, int height)
        {
            w = width;
            h = height;
            TranslationVel = 1.0f;
            RotationVel = 1.0f;

            Aspect = width / (float)height;
            Fov = 60.0f;

            Position = Point.Zero;
            mRight = Vector3.UnitX;
            mUp = Vector3.UnitY;
            mLook = Vector3.UnitZ;
        }

        public void Yaw(float angle)
        {


            //Matrix4x4 rot = Matrix4x4.CreateRotationY( angle );

            //mRight = Vector3.TransformNormal( ref mRight, ref rot );
            //mLook = Vector3.TransformNormal( ref mLook, ref rot );
        }

        public void Pitch(float angle)
        {
            //Matrix4x4 rot = Matrix4x4.CreateRotationAxis( mRight, angle );

            //mUp = Vector3.TransformNormal( ref mUp, ref rot );
            //mLook = Vector3.TransformNormal( ref mLook, ref rot );
        }

        #region ICamera Members

        public Point Position { get; set; }
        public Vector3 Target { get; set; }
        public Vector3 Right { get { return mRight * mUlength; } set { mRight = value; } }
        public Vector3 Up { get { return mUp * mVlength; } set { mUp = value; } }
        public Vector3 Look { get { return mLook; } set { mLook = value; } }

        public float Aspect { get; set; }
        public float Fov { get; set; }

        public float TranslationVel { get; set; }
        public float RotationVel { get; set; }

        public virtual void Dolly(float dt)
        {
            Position += mLook * (TranslationVel * dt);
        }

        public virtual void Pan(float dx, float dy)
        {
            dx *= TranslationVel;
            dy *= TranslationVel;

            Position += new Vector3(mRight.X * dx, 0.0f, mRight.Z * dx);
            Position += mUp * dy;
        }

        public virtual void Rotate(float dx, float dy)
        {
            Yaw(dx * RotationVel);
            Pitch(dy * RotationVel);
            BuildView();
        }

        public virtual void LookAt(Point pos, Point target, Vector3 up)
        {
            up.Normalize();

            Vector3 L = target - pos;
            L.Normalize();

            Vector3 R = Vector3.Cross(ref L, ref up);
            Vector3 U = Vector3.Cross(ref R, ref L);

            mUp = U;
            mRight = R;
            mLook = L;
            Position = pos;

            BuildView();
        }

        public virtual void BuildView()
        {
            float wlen = mLook.Length;
            mUlength = wlen * (float)Math.Tan(Fov * Math.PI / 180.0);
            mVlength = mUlength / Aspect;

            mLook.Normalize();

            mUp = Vector3.Cross(ref mRight, ref mLook);
            mUp.Normalize();

            mRight = Vector3.Cross(ref mLook, ref mUp);
            mRight.Normalize();
        }

        public virtual void CenterOnBoundingBox(AABB box)
        {
            var eye = box.Center;
            var target = box.Center;
            var up = new Vector3(0.0f, 1.0f, 0.0f);

            eye.Z += 2.0f * (box.Max - box.Min).Length;
            LookAt(eye, target, up);
        }

        public virtual void CenterOnBoundingBox(AABB box, float scale)
        {
            var eye = box.Center;
            var target = box.Center;
            var up = new Vector3(0.0f, 1.0f, 0.0f);

            eye.Z += 2.0f * (box.Max - box.Min).Length * scale;
            LookAt(eye, target, up);
        }

        public virtual void CenterOnBoundingBox(AABB box, Vector3 up, int axis, float scale)
        {
            var eye = box.Center;
            var target = box.Center;

            eye[axis] += 2.0f * (box.Max - box.Min).Length * scale;
            LookAt(eye, target, up);
        }

        public void GetRay(double xp, double yp, out IRay cameraRay)
        {
            var u = (float)(2.0f * xp / w - 1.0f);
            var v = (float)(1.0f - 2.0f * yp / h);

            Vector rdir = mRight * u + mUp * v + this.Look;
            var rorig = (Position + rdir * 0.1f);
            rdir.Normalize();
            cameraRay = new RayData(rorig, rdir);
        }

        public float ZNear
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public float ZFar
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        #endregion


        public void Sample(float x, float y, float u0, float u1, float u2, out IRay ray, out float pdf)
        {
            throw new NotImplementedException();
        }
    }
}
